<template>
  <div ref="containerRef" style="width: 100%; height: 100%"></div>
  <iframe
    src="./model/index-plush.html"
    frameborder="0"
    @load="onLoad"
    ref="iframeRef"
    v-if="open"
    style="opacity: 0"
  ></iframe>
</template>
<script setup>
import { ref, nextTick } from 'vue'
import * as THREE from 'three'
import { GUI } from '@/assets/js/lil-gui.module.min.js'
import { OrbitControls } from '@/assets/js/OrbitControls.js'
import { GLTFLoader } from '@/assets/js/GLTFLoader.js'

let open = ref(true)
let iframeRef = ref()
let containerRef = ref()
function onLoad() {
  window.addEventListener('message', async event => {
    try {
      let url = event?.data?.gltf
      let error = event?.data?.error
      if (error) {
        throw error
      }
      if (url) {
        // let url
        // if (content instanceof ArrayBuffer) {
        //   url = createURLFromArrayBuffer(content)
        // } else {
        //   url = createURLFromString(JSON.stringify(content, null, 2))
        //   // let link = document.createElement('a')
        //   // link.style.display = 'none'
        //   // document.body.appendChild(link) // Firefox workaround, see #6594
        //   // link.href = url
        //   // link.download = 'scene.glb'
        //   // link.click()
        // }
        await load(url)
        open.value = false
      }
    } catch (e) {
      console.log(e)
    }
  })
  iframeRef.value.contentWindow.postMessage('/test-file/imported.step')
}

function createPlane(color = 0xffff00, size, position) {
  let geometry = new THREE.PlaneGeometry(...size)
  let material = new THREE.MeshBasicMaterial({
    color,
    side: THREE.DoubleSide,
    transparent: true,
    opacity: 0.3,
  })
  let plane = new THREE.Mesh(geometry, material)
  plane.position.set(...position)
  return plane
}

let camera, scene, renderer

let params = {
  clipIntersection: true,
  planeConstant: 0,
  showHelpers: true,
  alphaToCoverage: true,
}

let clipPlanes = [
  new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0),
  new THREE.Plane(new THREE.Vector3(0, -1, 0), 0),
  new THREE.Plane(new THREE.Vector3(0, 0, -1), 0),
]
let clipInversionPlanes = [
  new THREE.Plane(new THREE.Vector3(1, 0, 0), 0),
  new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
  new THREE.Plane(new THREE.Vector3(0, 0, 1), 0),
]

let planeHelpers = []

let model

let size = new THREE.Vector3()
let center = new THREE.Vector3()
let box = new THREE.Box3()

function fitCameraToSelection(fitOffset = 2.2) {
  box.makeEmpty()
  for (let object of [group]) {
    box.expandByObject(object)
  }

  box.getSize(size)
  box.getCenter(center)
  let maxSize = Math.max(size.x, size.y, size.z)
  let fitHeightDistance =
    maxSize / (2 * Math.atan((Math.PI * camera.fov) / 360))
  let fitWidthDistance = fitHeightDistance / camera.aspect
  let distance = fitOffset * Math.max(fitHeightDistance, fitWidthDistance)

  let direction = controls.target
    .clone()
    .sub(camera.position)
    .normalize()
    .multiplyScalar(distance)

  controls.maxDistance = distance * 10
  controls.target.copy(center)

  camera.near = distance / 100
  camera.far = distance * 100
  camera.updateProjectionMatrix()

  camera.position.copy(controls.target).sub(direction)

  controls.update()
}

function getBoundingBox() {
  return box
}

function lookFront() {
  let distance = camera.position.distanceTo(new THREE.Vector3())
  controls.target.set(0, 0, 0)
  camera.position.set(0, 0, distance)
  controls.update()
}
function lookBack() {
  let distance = camera.position.distanceTo(new THREE.Vector3())
  controls.target.set(0, 0, 0)
  camera.position.set(0, 0, -distance)
  controls.update()
}
function lookLeft() {
  let distance = camera.position.distanceTo(new THREE.Vector3())
  controls.target.set(0, 0, 0)
  camera.position.set(-distance, 0, 0)
  controls.update()
}
function lookRight() {
  let distance = camera.position.distanceTo(new THREE.Vector3())
  controls.target.set(0, 0, 0)
  camera.position.set(distance, 0, 0)
  controls.update()
}
function lookTop() {
  let distance = camera.position.distanceTo(new THREE.Vector3())
  controls.target.set(0, 0, 0)
  camera.position.set(0, distance, 0)
  controls.update()
}
function lookBottom() {
  let distance = camera.position.distanceTo(new THREE.Vector3())
  controls.target.set(0, 0, 0)
  camera.position.set(0, -distance, 0)
  controls.update()
}

function zoom(cameraSpeed = 0.1) {
  let targetPosition = new THREE.Vector3()
  let cameraPosition = camera.position.clone()
  let direction = targetPosition.sub(cameraPosition)
  camera.position.add(direction.multiplyScalar(cameraSpeed))
  camera.lookAt(targetPosition)
  controls.update()
}

function meshVisible(visible) {
  group.children.find(({ myline }) => !myline).visible = visible
  render()
}
function lineVisible(visible) {
  group.children.find(({ myline }) => myline).visible = visible
  render()
}

function createGridHelper(size, center) {
  let gridHelper = new THREE.GridHelper(Math.max(size.x, size.z) * 10, 100)
  gridHelper.position.y = center.y - size.y
  gridHelper.material.opacity = 0.25
  gridHelper.material.transparent = true
  scene.add(gridHelper)
}

function createAxesHelper(size) {
  let axesHelper = new THREE.AxesHelper(Math.max(size.x, size.y, size.z) * 0.6) //The X axis is red. The Y axis is green. The Z axis is blue.
  scene.add(axesHelper)
}
function setClip(plan = 2, visible = true, constant = 0, inversion = false) {
  let clipFinalPlanes = inversion ? clipInversionPlanes : clipPlanes
  let clipPlane = clipFinalPlanes[plan]
  clipPlane.constant = inversion ? -constant : constant

  let planeHelper = planeHelpers[plan]
  planeHelper.visible = visible
  if (plan === 0) {
    planeHelper.position.setX(constant)
  }
  if (plan === 1) {
    planeHelper.position.setY(constant)
  }
  if (plan === 2) {
    planeHelper.position.setZ(constant)
  }

  group.traverse(child => {
    if (child.isMesh || child.isLine) {
      child.material.clippingPlanes = child.material.clippingPlanes.filter(
        clippingPlane => {
          return (
            clippingPlane !== clipPlane &&
            clipFinalPlanes.includes(clippingPlane)
          )
        },
      )
      if (visible) {
        child.material.clippingPlanes = [
          ...child.material.clippingPlanes,
          clipPlane,
        ]
      }
    }
  })

  render()
}

let group
let controls
function init(url) {
  let rect = containerRef.value.getBoundingClientRect()
  renderer = new THREE.WebGLRenderer({
    // antialias: true,
  })
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setSize(rect.width, rect.height)
  renderer.localClippingEnabled = true
  containerRef.value.appendChild(renderer.domElement)

  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xffffff)

  camera = new THREE.PerspectiveCamera(
    40,
    window.innerWidth / window.innerHeight,
    1,
    200,
  )

  camera.position.set(-1.5, 2.5, 3.0)

  controls = new OrbitControls(camera, renderer.domElement)
  controls.addEventListener('change', render) // use only if there is no animation loop
  controls.minDistance = 1
  controls.maxDistance = 10
  // controls.enablePan = false
  // controls.maxPolarAngle = Math.PI / 2 // 无限制，极角可以旋转整个球面
  // controls.minPolarAngle = -Math.PI * 2 // 从正下方开始
  console.log(controls)
  let light = new THREE.HemisphereLight(0xffffff, 0x999999, 2)
  light.position.set(0, 0, 1)
  let helper = new THREE.HemisphereLightHelper(light, 5)
  scene.add(helper)
  scene.add(light)
  // let light1 = new THREE.HemisphereLight(0xffffff, 0x999999, 2)
  // light.position.set(0, 0, 50)
  // let helper1 = new THREE.HemisphereLightHelper(light, 5)
  // scene.add(helper1)
  // scene.add(light1)

  group = new THREE.Group()

  let loader = new GLTFLoader()

  loader.load(url, async function (gltf) {
    model = gltf.scene
    // wait until the model can be added to the scene without blocking due to shader compilation

    group.add(model)
    model.traverse(child => {
      if (child.isMesh) {
        child.material = new THREE.MeshPhongMaterial({
          color: 0xaaaaaa,
          side: THREE.DoubleSide,
        })
      }
    })

    let line = model.clone()
    line.myline = true
    line.traverse(child => {
      if (child.isMesh) {
        // 创建 EdgesGeometry 以提取边缘
        let edges = new THREE.EdgesGeometry(child.geometry)
        // 创建线条材质
        let lineMaterial = new THREE.LineBasicMaterial({
          color: 0x000000,
          linewidth: 2,
        })
        // 创建 LineSegments 来渲染边缘
        let line = new THREE.LineSegments(edges, lineMaterial)
        // 将线条添加到 group 中
        child.parent.add(line)
        child.parent.remove(child)
      }
    })
    group.add(line)

    group.traverse(child => {
      if (child.isMesh || child.isLine) {
        // child.material.side = 2
        child.material.clippingPlanes = []
        // child.material.clipShadows = true // 可选：启用阴影裁剪
        child.material.needsUpdate = true // 更新材质
      }
    })
    scene.add(group)

    await renderer.compileAsync(model, camera, scene)

    fitCameraToSelection(2.2)

    createGridHelper(size, center)
    createAxesHelper(size)

    let planeHelper1 = createPlane(
      0xeeeeee,
      [size.z, size.y],
      [center.x, center.y, center.z],
    )
    planeHelper1.rotation.y = Math.PI / 2

    let planeHelper2 = createPlane(
      0xeeeeee,
      [size.z, size.x],
      [center.x, center.y, center.z],
    )
    planeHelper2.rotation.x = Math.PI / 2
    planeHelper2.rotation.z = Math.PI / 2

    let planeHelper3 = createPlane(
      0xeeeeee,
      [size.x, size.y],
      [center.x, center.y, center.z],
    )
    planeHelper1.visible = false
    planeHelper2.visible = false
    planeHelper3.visible = false
    scene.add(planeHelper1)
    scene.add(planeHelper2)
    scene.add(planeHelper3)

    planeHelpers = [planeHelper1, planeHelper2, planeHelper3]

    clipPlanes[0].constant = center.x
    clipPlanes[1].constant = center.y
    clipPlanes[2].constant = center.z
    render()
    console.log(controls)
  })

  // gui

  let gui = new GUI()

  // gui.add(params, 'alphaToCoverage').onChange(function (value) {
  //   group.children.forEach(c => {
  //     c.material.alphaToCoverage = Boolean(value)
  //     c.material.needsUpdate = true
  //   })

  //   render()
  // })

  gui
    .add(params, 'clipIntersection')
    .name('zoomIn')
    .onChange(function (value) {
      zoom()
    })
  gui
    .add(params, 'clipIntersection')
    .name('zoomOut')
    .onChange(function (value) {
      zoom(-0.1)
    })
  gui
    .add(params, 'clipIntersection')
    .name('lookFront')
    .onChange(function (value) {
      lookFront()
    })
  gui
    .add(params, 'clipIntersection')
    .name('lookBack')
    .onChange(function (value) {
      lookBack()
    })
  gui
    .add(params, 'clipIntersection')
    .name('lookLeft')
    .onChange(function (value) {
      lookLeft()
    })
  gui
    .add(params, 'clipIntersection')
    .name('lookRight')
    .onChange(function (value) {
      lookRight()
    })
  gui
    .add(params, 'clipIntersection')
    .name('lookTop')
    .onChange(function (value) {
      lookTop()
    })
  gui
    .add(params, 'clipIntersection')
    .name('lookBottom')
    .onChange(function (value) {
      lookBottom()
    })
  gui
    .add(params, 'clipIntersection')
    .name('lineVisible')
    .onChange(function (value) {
      lineVisible(value)
    })
  gui
    .add(params, 'clipIntersection')
    .name('meshVisible')
    .onChange(function (value) {
      meshVisible(value)
    })
  let constant
  gui
    .add(params, 'clipIntersection')
    .name('inversion123')
    .onChange(function (value) {
      console.log(value)
      window.inversion123 = value
      console.log(constant)
      setClip(2, true, constant, window.inversion123)
    })
  window.inversion123 = false

  gui
    .add(params, 'planeConstant', -100, 100)
    .step(0.01)
    .name('plane constant z')
    .onChange(function (value) {
      constant = value
      setClip(2, true, value, window.inversion123)
    })
  gui
    .add(params, 'planeConstant', -100, 100)
    .step(0.01)
    .name('plane constant y')
    .onChange(function (value) {
      setClip(1, true, value)
    })
  gui
    .add(params, 'planeConstant', -100, 100)
    .step(0.01)
    .name('plane constant x')
    .onChange(function (value) {
      setClip(0, true, value)
    })

  // gui
  //   .add(params, 'showHelpers')
  //   .name('show helpers')
  //   .onChange(function (value) {
  //     helpers.visible = value

  //     render()
  //   })

  window.addEventListener('resize', onWindowResize)
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()

  renderer.setSize(window.innerWidth, window.innerHeight)

  render()
}

function render() {
  renderer.render(scene, camera)
}

async function load(url) {
  await nextTick()

  init(url)
  render()
}

defineExpose({
  zoom,
  lookFront,
  lookBack,
  lookLeft,
  lookRight,
  lookTop,
  lookBottom,
  lineVisible,
  meshVisible,
  setClip,
  getBoundingBox,
})
</script>
